---
title: csharp的最小api
sidebar_position: 13
---

C# 在 dotnet6 之后添加了 minimalapi, 这允许开发者只需使用少量代码即可构建出一个可用的api程序

## 创建项目

可在命令行中执行以下代码创建项目
```
dotnet new webapi -minimal -n myapp
```

根据使用的sdk版本, 创建的项目模板可能会有所不同, 比如我在用的sdk版本是8.0.111, 模板中program.cs的内容如下:

```csharp
var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast =  Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast")
.WithOpenApi();

app.Run();

record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
```

上面创建了一个返回列表的接口, 不过这些代码对于讲解来说还是有点多余, 可将 `Program.cs` 里面的内容都删掉, 从头开始写起

## 创建接口

```csharp {2}
var app = WebApplication.CreateBuilder(args).Build();
app.MapGet("/", () => "Hello, World!");
app.Run();
```

其实只需要三行即可在启动端口的根目录下直接创建一个api接口, 启动程序后访问对应站点即可看到浏览器上输出的 `Hello, World!`

在miniapi中可以非常简单地创建 restful api

* `app.MapPost`
* `app.MapDelete`
* `app.MapPut`
* `app.MapGet`

通过以上的四个方法可以完成增删改查四种基本的restful操作

## 参数匹配

由于miniapi接收的是委托类型的参数, 所以在定义参数时与之前常用的controller不同

```csharp
app.MapGet(route, ([FromQuery] string data) => data);
```

`[FromQuery]` 可以说是最基础的一种参数接收形式, 用于接收请求路径`?`之后的参数内容, 例如`http://{host}:{port}/getdata?data=123`, 委托中获取到的`data`就会是`123`

不过有一点需要注意, 之前在 controller 中可以指定自定义类型来接受多个参数, 比如`[FromQuery]InputData data`这种的, 可以一次性接收多个参数, 但是在 miniapi 中不能直接这么使用, `InputData`必须要有将 `string` 类型的参数字符串转换为 `InputData` 的 `TryPrase` 方法, 这样就显得尤为麻烦

因此如果想要通过 `[FromQuery]` 接收多个参数, 最好还是将参数拆开定义

```csharp
app.MapGet(route, ([FromQuery] string data, [FromQuery] int num) => $@"{data}:{num}");
```

不过挺好的是 `[FromBody]` 的用法和以前没啥区别, 还是一样的非常方便

```csharp
app.MapPost(route, ([FromBody] InputData data) => $@"{data.Data}:{data.Num}");
public class InputData
{
    public string Data { get; set; }
    public string Num { get; set; }
}
```

## 使用依赖注入

众所周知在现在的dotnet中, 依赖注入(DI)是必不可少的, 虽然 miniapi 很简单, 但还是可以正常用上的

```csharp {4}
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(new Config { Data = "233" });
...
app.MapPost(route, ([FromServices] Config config) => config.Data);
app.Run();
public class Config
{
    public string Data { get; set; }
}
```

在委托的参数列表里可以使用 `[FromServices]` 从依赖注入容器中获取注册过的实例

:::tip
其实在 controller 中也可以使用这种方式注入实例, 只不过在正常情况下一般还是用构造函数注入, 但 miniapi 中只能用这种方式
:::

这种注入方式讲究的就是用啥注入啥, 但是在业务复杂并且需要注入的实例数量比较多的时候, 委托的参数列表就会显得太过冗长, 如果接口数量也同时上升, 那么重复注入的情况就会出现很多次, 这种情况下可能就不如 controller 方便了

## 总结

miniapi 虽然能满足一些简单的需求, 但对于复杂的业务, 还是建议使用 controller 来实现
